package com.example.requestdemo.configs;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 初始化线程池
 *
 * @author xxx
 */
@Slf4j
@Component
public class ExecutorHelper {

    @Getter
    private final ExecutorService defaultExecutorService;

    /**
     * corePoolSize    核心线程数，池中所保存的线程数，包括空闲线程。活动线程小于corePoolSize时则直接创建，大于等于时则添加到 workQueue(工作队列) 中等待执行。
     * maximumPoolSize    池中允许的最大线程数。每个线程都有自己约定大小的 workQueue(工作队列)，如果corePoolSize 已经满了，而且所有核心线程的 workQueue 也已经排满了，当前线程池活动线程总数又小于maximumPoolSize时，则创建新的线程执行任务。如果线程池中活动线程数已经超过maximumPoolSize了，则执行饱和策略(handler)
     * keepAliveTime    当线程数大于核心线程时，此为终止当前多余的空闲线程等待新任务的最长时间。最大线程数量大于核心线程数时，当线程执行任务完成后，就要关闭多余的线程(它们此时处于空闲状态)，直到线程池中线程的数量恢复到 corePoolSize 的大小。为了提高线程的利用效率，可约定空闲线程保持存活的时间，大小为 keepAliveTime。对于数量多，时间短的任务，可以适当将 keepAliveTime 放大一点。
     * unit    keepAliveTime 参数的时间单位。
     * workQueue    此工作队列仅保持由 execute 方法提交的 Runnable 任务，每个活动(工作)线程都有一个工作队列，然后从这里获取任务依次进行执行
     * handler    当线程池中线程总数已经超过了maximumPoolSize，且每个线程的workQueue(工作队列)也已经排满时，则执行饱和策略。通俗的说是线程池中的任务已经爆满了，此时对于新加的任务应该如何处理？可以参考《线程池理论》中的饱和策略部分。
     */
    public ExecutorHelper() {
        Runtime runtime = Runtime.getRuntime();
        // 线程池维护线程的最少数量
        int corePoolSize = runtime.availableProcessors();
        // 线程池维护线程的最大数量
        int maximumPoolSize = Math.max(corePoolSize, 6);
        // 线程池维护线程所允许的空闲时间
        long keepAliveTime = 10L;
        // 线程池维护线程所允许的空闲时间的单位
        TimeUnit unit = TimeUnit.SECONDS;
        int capacity = 1000;
        // 线程池所使用的缓冲队列
        LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(capacity);
        // 线程池对拒绝任务的处理策略
        ThreadPoolExecutor.CallerRunsPolicy handler = new ThreadPoolExecutor.CallerRunsPolicy();

        // ThreadPoolExecutor 对线程池和队列的使用方式如下：
        //   1、从线程池中获取可用线程执行任务，如果没有可用线程则使用ThreadFactory创建新的线程，直到线程数达到corePoolSize限制
        //   2、线程池线程数达到corePoolSize以后，新的任务将被放入队列，直到队列不能再容纳更多的任务
        //   3、当队列不能再容纳更多的任务以后，会创建新的线程，直到线程数达到maximumPoolSize限制
        //   4、线程数达到maximumPoolSize限制以后新任务会被拒绝执行，调用 RejectedExecutionHandler 进行处理

        log.info("==> 线程池维护线程的最少数量 {} | 线程池维护线程的最大数量:{} | 线程池维护线程所允许的空闲时间:{} | 线程池维护线程所允许的空闲时间的单位:{} | 线程池所使用的缓冲队列：{} | 线程池对拒绝任务的处理策略：{}", corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        defaultExecutorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }
}
